import { reactive } from '../chapter5-reactiveWithObj/4.array-proxy'
import { effect } from '../chapter5-reactiveWithObj/reactive-system'

const WRAP_KEY = '__v_isRef'

// 原始值的响应式方案
function ref(val) {
  const wrapper = {
    value: val,
  }
  // 定义一个不可枚举属性 __v_isRef来区分ref还是reactive
  Object.defineProperty(wrapper, WRAP_KEY, {
    value: true,
  })
  return reactive(wrapper)
}

function toRef(obj, key) {
  const wrapper = {
    get value() {
      return obj[key]
    },
    set value(newval) {
      obj[key] = newval
    },
  }
  Object.defineProperty(wrapper, WRAP_KEY, {
    value: true,
  })
  return wrapper
}

function toRefs(obj) {
  const ret = {}
  for (const key in obj) {
    ret[key] = toRef(obj, key)
  }
  return ret
}

// 自动脱ref, 老是写.value太麻烦了
function proxyRefs(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      const value = Reflect.get(target, key, receiver)
      return value[WRAP_KEY] ? value.value : value
    },
    set(target, key, value, receiver) {
      const val = target[key]
      // 是ref
      if (val[WRAP_KEY]) {
        val.value = value
        return true
      }
      return Reflect.set(target, key, value, receiver)
    },
  })
}

// ====================== 1. 原始值的响应式 ======================
/* const valid = ref(false)

effect(() => {
  console.log(valid.value) // 通过value访问值
})
valid.value = true // 通过value来设置值 */

// ====================== 2. 响应式丢失 ======================
const obj = reactive({ foo: 1, bar: 2 })

// 这样会丢失响应式
/* const newObj = {
  ...obj,
}
effect(() => {
  console.log(newObj.foo)
})

newObj.foo = 3 // 无法触发响应式 */

// 使用toRef
/* const fooRef = toRef(obj, 'foo')
effect(() => {
  console.count('fooRef')
  console.log(fooRef.value)
})
fooRef.value = 5

const newObj = {
  foo: toRef(obj, 'foo'),
  bar: toRef(obj, 'bar'),
}
effect(() => {
  console.log(newObj.foo.value)
})
newObj.foo.value = 3 */

// 使用toRefs
/* const { foo } = toRefs(obj)
effect(() => {
  console.count('fooRef')
  console.log(foo.value)
})
foo.value = 3 */

// 使用自动脱ref + toRefs, 类似在Vue3中的模板中使用响应式ref不用写value
const newObj = proxyRefs(toRefs(obj))
effect(() => {
  console.log(newObj.foo)
})
newObj.foo = 3

// 但是无法这么使用，因为foo又是丢失响应式，所以还是得取舍。
/* let { foo } = proxyRefs(toRefs(obj))
effect(() => {
  console.log(foo)
})
foo = 3 */
